using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[DisallowMultipleComponent]
public class GameManager : SingletonMonobehaviour<GameManager>
{
    #region Header GAMEOBJECT REFERENCES

    [Space(10)]
    [Header("游戏对象引用")]

    #endregion Header GAMEOBJECT REFERENCES

    #region Tooltip

    [Tooltip("在层次结构中填充暂停菜单游戏对象")]

    #endregion Tooltip

    [SerializeField]
    private GameObject pauseMenu;

    #region Tooltip

    [Tooltip("在 FadeScreenUI 中使用 MessageText textmeshpro 组件填充")]

    #endregion Tooltip

    [SerializeField]
    private TextMeshProUGUI messageTextTMP;

    #region Tooltip

    [Tooltip("在 FadeScreenUI 中使用 FadeImage canvasgroup 组件进行填充")]

    #endregion Tooltip

    [SerializeField]
    private CanvasGroup canvasGroup;


    #region Header DUNGEON LEVELS

    [Space(10)]
    [Header("地牢级别")]

    #endregion Header DUNGEON LEVELS

    #region Tooltip

    [Tooltip("填充地牢级别的可编写脚本的对象")]

    #endregion Tooltip

    [SerializeField]
    private List<DungeonLevelSO> dungeonLevelList;

    #region Tooltip

    [Tooltip("填充用于测试的起始地牢级别，第一级别 = 0")]

    #endregion Tooltip

    [SerializeField]
    private int currentDungeonLevelListIndex = 0;

    private Room currentRoom;
    private Room previousRoom;
    private PlayerDetailsSO playerDetails;
    private Player player;

    [HideInInspector] public GameState gameState;
    [HideInInspector] public GameState previousGameState;
    private long gameScore;
    private int scoreMultiplier;
    private InstantiatedRoom bossRoom;
    private bool isFading = false;

    protected override void Awake()
    {
        // 调用基类
        base.Awake();

        // 设置玩家详细信息 - 保存在主菜单中的当前玩家可编程对象中
        playerDetails = GameResources.Instance.currentPlayer.playerDetails;

        // 实例化玩家
        InstantiatePlayer();
    }

    private void OnEnable()
    {
        // 订阅房间更改事件。
        StaticEventHandler.OnRoomChanged += StaticEventHandler_OnRoomChanged;

        // 订阅房间敌人击败事件
        StaticEventHandler.OnRoomEnemiesDefeated += StaticEventHandler_OnRoomEnemiesDefeated;

        // 订阅积分活动
        StaticEventHandler.OnPointsScored += StaticEventHandler_OnPointsScored;

        // 订阅分数乘数事件
        StaticEventHandler.OnMultiplier += StaticEventHandler_OnMultiplier;

        // 订阅玩家被毁事件
        player.destroyedEvent.OnDestroyed += Player_OnDestroyed;
    }

    private void OnDisable()
    {
        // 取消订阅房间更改事件
        StaticEventHandler.OnRoomChanged -= StaticEventHandler_OnRoomChanged;

        // 取消订阅房间敌人击败活动
        StaticEventHandler.OnRoomEnemiesDefeated -= StaticEventHandler_OnRoomEnemiesDefeated;

        // 取消订阅得分活动
        StaticEventHandler.OnPointsScored -= StaticEventHandler_OnPointsScored;

        // 取消订阅分数乘数活动
        StaticEventHandler.OnMultiplier -= StaticEventHandler_OnMultiplier;

        // 从玩家被毁事件中取消
        player.destroyedEvent.OnDestroyed -= Player_OnDestroyed;
    }

    /// <summary>
    /// 处理房间更改事件
    /// </summary>
    private void StaticEventHandler_OnRoomChanged(RoomChangedEventArgs roomChangedEventArgs)
    {
        SetCurrentRoom(roomChangedEventArgs.room);
    }

    /// <summary>
    /// 处理房间敌人击败事件
    /// </summary>
    private void StaticEventHandler_OnRoomEnemiesDefeated(RoomEnemiesDefeatedArgs roomEnemiesDefeatedArgs)
    {
        RoomEnemiesDefeated();
    }

    /// <summary>
    /// 处理得分事件
    /// </summary>
    private void StaticEventHandler_OnPointsScored(PointsScoredArgs pointsScoredArgs)
    {
        // 提高分数
        gameScore += pointsScoredArgs.points * scoreMultiplier;

        // 呼叫分数更改事件
        StaticEventHandler.CallScoreChangedEvent(gameScore, scoreMultiplier);
    }

    /// <summary>
    /// 处理分数乘数事件
    /// </summary>
    private void StaticEventHandler_OnMultiplier(MultiplierArgs multiplierArgs)
    {
        if (multiplierArgs.multiplier)
        {
            scoreMultiplier++;
        }
        else
        {
            scoreMultiplier--;
        }

        // 限制在 1 到 30 之间
        scoreMultiplier = Mathf.Clamp(scoreMultiplier, 1, 30);

        // 呼叫分数更改事件
        StaticEventHandler.CallScoreChangedEvent(gameScore, scoreMultiplier);
    }

    /// <summary>
    /// 在场景中的位置创建玩家
    /// </summary>
    private void InstantiatePlayer()
    {
        // 实例化玩家
        GameObject playerGameObject = Instantiate(playerDetails.playerPrefab);

        // 初始化玩家
        player = playerGameObject.GetComponent<Player>();

        player.Initialize(playerDetails);
    }

    /// <summary>
    /// 处理玩家被毁事件
    /// </summary>
    private void Player_OnDestroyed(DestroyedEvent destroyedEvent, DestroyedEventArgs destroyedEventArgs)
    {
        previousGameState = gameState;
        gameState = GameState.gameLost;
    }

    private void Start()
    {
        previousGameState = GameState.gameStarted;
        gameState = GameState.gameStarted;

        // 将分数设置为零
        gameScore = 0;

        // 将屏幕设置为黑色
        StartCoroutine(Fade(0f, 1f, 0f, Color.black));
    }

    private void Update()
    {
        HandleGameState();

        // if (Input.GetKeyDown(KeyCode.P))
        // {
        //     gameState = GameState.gameStarted;
        // }
    }

    /// <summary>
    /// 处理游戏状态
    /// </summary>
    private void HandleGameState()
    {
        // Handle game state
        switch (gameState)
        {
            case GameState.gameStarted:

                // 玩第一关
                PlayDungeonLevel(currentDungeonLevelListIndex);

                gameState = GameState.playingLevel;

                //触发房间的敌人被击败，因为我们从没有敌人的入口开始（以防万一你有一个只有一个老板房间的关卡！)
                RoomEnemiesDefeated();

                break;

            // 在玩关卡时，请处理地牢概览地图的 Tab 键。
            case GameState.playingLevel:

                if (Input.GetKeyDown(KeyCode.Escape))
                {
                    PauseGameMenu();
                }

                if (Input.GetKeyDown(KeyCode.Tab))
                {
                    DisplayDungeonOverviewMap();
                }

                break;

            // 在与敌人交战时，请处理暂停菜单的逃生键
            case GameState.engagingEnemies:

                if (Input.GetKeyDown(KeyCode.Escape))
                {
                    PauseGameMenu();
                }

                break;


            // 如果在地牢概览地图中，请释放 Tab 键以清除地图
            case GameState.dungeonOverviewMap:

                // Key released
                if (Input.GetKeyUp(KeyCode.Tab))
                {
                    // 清除地牢OverviewMap
                    DungeonMap.Instance.ClearDungeonOverViewMap();
                }

                break;

            // 在玩关卡时，在BOSS参与之前，处理地牢概览地图的Tab键。
            case GameState.bossStage:

                if (Input.GetKeyDown(KeyCode.Escape))
                {
                    PauseGameMenu();
                }

                if (Input.GetKeyDown(KeyCode.Tab))
                {
                    DisplayDungeonOverviewMap();
                }

                break;

            // 在与Boss交战时，手柄退出键以用于暂停菜单
            case GameState.engagingBoss:

                if (Input.GetKeyDown(KeyCode.Escape))
                {
                    PauseGameMenu();
                }

                break;

            // 处理正在完成的关卡
            case GameState.levelCompleted:

                // 显示级别完成的文本
                StartCoroutine(LevelCompleted());

                break;

            // 处理正在赢得的游戏（仅触发此操作一次 - 测试前一个游戏状态以执行此操作）
            case GameState.gameWon:

                if (previousGameState != GameState.gameWon)
                    StartCoroutine(GameWon());

                break;

            // 处理丢失的游戏（仅触发一次 - 测试前一个游戏状态以执行此操作）
            case GameState.gameLost:

                if (previousGameState != GameState.gameLost)
                {
                    StopAllCoroutines(); // 如果您在被杀死时清除了关卡，请阻止消息
                    StartCoroutine(GameLost());
                }

                break;

            // 重新启动游戏
            case GameState.restartGame:

                RestartGame();

                break;

            // 如果游戏暂停并显示暂停菜单，则再次按 Escape 将清除暂停菜单
            case GameState.gamePaused:
                if (Input.GetKeyDown(KeyCode.Escape))
                {
                    PauseGameMenu();
                }

                break;
        }
    }

    /// <summary>
    /// 设置玩家所在的当前房间
    /// </summary>
    public void SetCurrentRoom(Room room)
    {
        previousRoom = currentRoom;
        currentRoom = room;

        //// Debug
        //Debug.Log(room.prefab.name.ToString());
    }

    /// <summary>
    /// 房间敌人被清除 - 测试是否所有地牢房间都已清除敌人 - 如果是，请加载下一个地牢游戏级别
    /// </summary>
    private void RoomEnemiesDefeated()
    {
        // 将地牢初始化为正在清理 - 但随后测试每个房间
        bool isDungeonClearOfRegularEnemies = true;
        bossRoom = null;

        // 循环穿过所有地牢房间，看看是否清除了敌人
        foreach (KeyValuePair<string, Room> keyValuePair in DungeonBuilder.Instance.dungeonBuilderRoomDictionary)
        {
            // 暂时跳过老板房间
            if (keyValuePair.Value.roomNodeType.isBossRoom)
            {
                bossRoom = keyValuePair.Value.instantiatedRoom;
                continue;
            }

            // 检查其他房间是否已清除敌人
            if (!keyValuePair.Value.isClearedOfEnemies)
            {
                isDungeonClearOfRegularEnemies = false;
                break;
            }
        }

        // 设置游戏状态
        // 如果地牢级别完全清除(即地牢清除了BOSS之外没有BOSS房间，或者地牢除了BOSS而已清除，BOSS房间也被清除)
        if ((isDungeonClearOfRegularEnemies && bossRoom == null) ||
            (isDungeonClearOfRegularEnemies && bossRoom.room.isClearedOfEnemies))
        {
            // 那有更多的地牢级别吗
            if (currentDungeonLevelListIndex < dungeonLevelList.Count - 1)
            {
                gameState = GameState.levelCompleted;
            }
            else
            {
                gameState = GameState.gameWon;
            }
        }
        // 否则，如果地牢级别除了老板房间之外被清理干净
        else if (isDungeonClearOfRegularEnemies)
        {
            gameState = GameState.bossStage;

            StartCoroutine(BossStage());
        }
    }

    /// <summary>
    /// 暂停游戏菜单 - 也称为暂停菜单上的恢复游戏按钮
    /// </summary>
    public void PauseGameMenu()
    {
        if (gameState != GameState.gamePaused)
        {
            pauseMenu.SetActive(true);
            GetPlayer().playerControl.DisablePlayer();

            // 设置游戏状态
            previousGameState = gameState;
            gameState = GameState.gamePaused;
        }
        else if (gameState == GameState.gamePaused)
        {
            pauseMenu.SetActive(false);
            GetPlayer().playerControl.EnablePlayer();

            // 设置游戏状态
            gameState = previousGameState;
            previousGameState = GameState.gamePaused;
        }
    }

    /// <summary>
    /// 地牢地图屏幕显示
    /// </summary>
    private void DisplayDungeonOverviewMap()
    {
        // 如果淡出，则返回
        if (isFading)
            return;

        // 显示地牢OverviewMap
        DungeonMap.Instance.DisplayDungeonOverViewMap();
    }

    private void PlayDungeonLevel(int dungeonLevelListIndex)
    {
        // 为关卡建造地牢
        bool dungeonBuiltSucessfully = DungeonBuilder.Instance.GenerateDungeon(dungeonLevelList[dungeonLevelListIndex]);

        if (!dungeonBuiltSucessfully)
        {
            Debug.LogError("无法从指定的房间和节点图构建地牢");
        }


        // 调用房间已更改的静态事件。
        StaticEventHandler.CallRoomChangedEvent(currentRoom);

        // 将玩家设置在房间大致中间
        player.gameObject.transform.position = new Vector3((currentRoom.lowerBounds.x + currentRoom.upperBounds.x) / 2f,
            (currentRoom.lowerBounds.y + currentRoom.upperBounds.y) / 2f, 0f);

        // 在离玩家最近的房间中获取最近的出生点
        player.gameObject.transform.position =
            HelperUtilities.GetSpawnPositionNearestToPlayer(player.gameObject.transform.position);

        // 显示地牢级别文本
        StartCoroutine(DisplayDungeonLevelText());
        //
        // //// ** Demo code
        // //RoomEnemiesDefeated();
    }

    /// <summary>
    /// 显示地牢级别文本
    /// </summary>
    private IEnumerator DisplayDungeonLevelText()
    {
        // 将屏幕设置为黑色
        StartCoroutine(Fade(0f, 1f, 0f, Color.black));

        GetPlayer().playerControl.DisablePlayer();

        string messageText = "等级 " + (currentDungeonLevelListIndex + 1).ToString() + "\n\n" +
                             dungeonLevelList[currentDungeonLevelListIndex].levelName.ToUpper();

        yield return StartCoroutine(DisplayMessageRoutine(messageText, Color.white, 2f));

        GetPlayer().playerControl.EnablePlayer();

        // 淡入
        yield return StartCoroutine(Fade(1f, 0f, 2f, Color.black));
    }

    /// <summary>
    /// 显示 displaySeconds 的消息文本 - 如果 displaySeconds =0，则消息将显示，直到按下返回键
    /// </summary>
    private IEnumerator DisplayMessageRoutine(string text, Color textColor, float displaySeconds)
    {
        // 设置文本
        messageTextTMP.SetText(text);
        messageTextTMP.color = textColor;

        // 显示给定时间的消息
        if (displaySeconds > 0f)
        {
            float timer = displaySeconds;

            while (timer > 0f && !Input.GetKeyDown(KeyCode.Return))
            {
                timer -= Time.deltaTime;
                yield return null;
            }
        }
        // 否则，在按下返回按钮之前显示消息
        else
        {
            while (!Input.GetKeyDown(KeyCode.Return))
            {
                yield return null;
            }
        }

        yield return null;

        // 清理文本
        messageTextTMP.SetText("");
    }

    /// <summary>
    /// 进入BOSS阶段
    /// </summary>
    private IEnumerator BossStage()
    {
        // 激活Boss房间
        bossRoom.gameObject.SetActive(true);

        // 解锁Boss房间
        bossRoom.UnlockDoors(0f);

        yield return new WaitForSeconds(2f);

        // 在画布中淡入淡出以显示文本消息
        yield return StartCoroutine(Fade(0f, 1f, 2f, new Color(0f, 0f, 0f, 0.4f)));

        // 显示Boss消息
        yield return StartCoroutine(DisplayMessageRoutine(
            "干的好  " + GameResources.Instance.currentPlayer.playerName +
            "!  你活了下来 ....迄今\n\n现在找到并击败BOSS....祝你好运!", Color.white, 5f));

        // 淡出画布
        yield return StartCoroutine(Fade(1f, 0f, 2f, new Color(0f, 0f, 0f, 0.4f)));
    }

    /// <summary>
    /// 将级别显示为已完成 - 加载下一级
    /// </summary>
    private IEnumerator LevelCompleted()
    {
        // 玩下一关
        gameState = GameState.playingLevel;

        // 等待 2 秒
        yield return new WaitForSeconds(2f);

        // 在画布中淡入淡出以显示文本消息
        yield return StartCoroutine(Fade(0f, 1f, 2f, new Color(0f, 0f, 0f, 0.4f)));

        // 显示级别已完成
        yield return StartCoroutine(DisplayMessageRoutine(
            "干的好 " + GameResources.Instance.currentPlayer.playerName + "! \n\n你已经在这个地牢关卡中幸存下来了",
            Color.white, 5f));

        yield return StartCoroutine(DisplayMessageRoutine(
            "收集任何战利品 ....然后按 RETURN 键\n\n去到下一层地牢中", Color.white, 5f));

        // 淡出画布
        yield return StartCoroutine(Fade(1f, 0f, 2f, new Color(0f, 0f, 0f, 0.4f)));

        // 当玩家按下返回键时，进入下一关
        while (!Input.GetKeyDown(KeyCode.Return))
        {
            yield return null;
        }

        yield return null; // 为避免输入被检测两次

        // 将指数提升到一个新的水平
        currentDungeonLevelListIndex++;

        PlayDungeonLevel(currentDungeonLevelListIndex);
    }

    /// <summary>
    /// Fade Canvas 组
    /// </summary>
    public IEnumerator Fade(float startFadeAlpha, float targetFadeAlpha, float fadeSeconds, Color backgroundColor)
    {
        isFading = true;

        Image image = canvasGroup.GetComponent<Image>();
        image.color = backgroundColor;

        float time = 0;

        while (time <= fadeSeconds)
        {
            time += Time.deltaTime;
            canvasGroup.alpha = Mathf.Lerp(startFadeAlpha, targetFadeAlpha, time / fadeSeconds);
            yield return null;
        }

        isFading = false;
    }

    /// <summary>
    /// 比赛赢了
    /// </summary>
    private IEnumerator GameWon()
    {
        previousGameState = GameState.gameWon;

        // 禁用角色
        GetPlayer().playerControl.DisablePlayer();

        int rank = HighScoreManager.Instance.GetRank(gameScore);

        string rankText;

        // 测试分数是否在排名中
        if (rank > 0 && rank <= Settings.numberOfHighScoresToSave)
        {
            rankText = "您的分数已排名 " + rank.ToString("#0") + " 在顶部 " + Settings.numberOfHighScoresToSave.ToString("#0");

            string name = GameResources.Instance.currentPlayer.playerName;

            if (name == "")
            {
                name = playerDetails.playerCharacterName.ToUpper();
            }

            // 更新分数
            HighScoreManager.Instance.AddScore(
                new Score()
                {
                    playerName = name,
                    levelDescription = "水平 " + (currentDungeonLevelListIndex + 1).ToString() + " - " +
                                       GetCurrentDungeonLevel().levelName.ToUpper(),
                    playerScore = gameScore
                }, rank);
        }
        else
        {
            rankText = "您的分数未排在前列 " + Settings.numberOfHighScoresToSave.ToString("#0");
        }

        // 等待 1 秒
        yield return new WaitForSeconds(1f);

        // 淡出
        yield return StartCoroutine(Fade(0f, 1f, 2f, Color.black));

        // 显示游戏赢了
        yield return StartCoroutine(DisplayMessageRoutine(
            "WELL DONE " + GameResources.Instance.currentPlayer.playerName + "! 你打通了地牢",
            Color.white, 3f));

        yield return StartCoroutine(DisplayMessageRoutine("你得了 " + gameScore.ToString("###,###0") + "分\n\n" + rankText,
            Color.white, 4f));

        yield return StartCoroutine(DisplayMessageRoutine("按回车键重新开始游戏", Color.white, 0f));

        // 设置游戏状态以重新启动游戏
        gameState = GameState.restartGame;
    }

    /// <summary>
    /// Game Lost
    /// </summary>
    private IEnumerator GameLost()
    {
        previousGameState = GameState.gameLost;

        // 禁用角色
        GetPlayer().playerControl.DisablePlayer();

        // 获取排名
        int rank = HighScoreManager.Instance.GetRank(gameScore);
        string rankText;

        // 测试分数是否在排名中
        if (rank > 0 && rank <= Settings.numberOfHighScoresToSave)
        {
            rankText = "您的分数已排名 " + rank.ToString("#0") + " 在顶部 " + Settings.numberOfHighScoresToSave.ToString("#0");

            string name = GameResources.Instance.currentPlayer.playerName;

            if (name == "")
            {
                name = playerDetails.playerCharacterName.ToUpper();
            }

            // 更新分数
            HighScoreManager.Instance.AddScore(
                new Score()
                {
                    playerName = name,
                    levelDescription = "水平 " + (currentDungeonLevelListIndex + 1).ToString() + " - " +
                                       GetCurrentDungeonLevel().levelName.ToUpper(),
                    playerScore = gameScore
                }, rank);
        }
        else
        {
            rankText = "您的分数未排在前列 " + Settings.numberOfHighScoresToSave.ToString("#0");
        }

        // 等待 1 秒
        yield return new WaitForSeconds(1f);

        // 淡出
        yield return StartCoroutine(Fade(0f, 1f, 2f, Color.black));

        // 禁用敌人（FindObjectsOfType 资源匮乏 - 但在游戏结束时可以使用）
        Enemy[] enemyArray = GameObject.FindObjectsOfType<Enemy>();
        foreach (Enemy enemy in enemyArray)
        {
            enemy.gameObject.SetActive(false);
        }

        // 显示游戏失败
        yield return StartCoroutine(DisplayMessageRoutine(
            "运气真差 " + GameResources.Instance.currentPlayer.playerName + "! 你已经屈服于地牢",
            Color.white, 2f));

        yield return StartCoroutine(
            DisplayMessageRoutine("你得了 " + gameScore.ToString("###,###0") + "分\n\n" + rankText, Color.white, 4f));

        yield return StartCoroutine(DisplayMessageRoutine("按回车键重新开始游戏", Color.white, 0f));

        // 设置游戏状态以重新启动游戏
        gameState = GameState.restartGame;
    }

    /// <summary>
    /// 重新启动游戏
    /// </summary>
    private void RestartGame()
    {
        SceneManager.LoadScene("MainMenuScene");
    }

    /// <summary>
    /// 获取玩家
    /// </summary>
    public Player GetPlayer()
    {
        return player;
    }

    /// <summary>
    /// 获取玩家小地图图标
    /// </summary>
    public Sprite GetPlayerMiniMapIcon()
    {
        return playerDetails.playerMiniMapIcon;
    }

    /// <summary>
    /// 获取玩家当前所在的房间
    /// </summary>
    public Room GetCurrentRoom()
    {
        return currentRoom;
    }

    /// <summary>
    /// 获取当前的地牢级别
    /// </summary>
    public DungeonLevelSO GetCurrentDungeonLevel()
    {
        return dungeonLevelList[currentDungeonLevelListIndex];
    }

    #region Validation

#if UNITY_EDITOR

    private void OnValidate()
    {
        HelperUtilities.ValidateCheckNullValue(this, nameof(pauseMenu), pauseMenu);
        HelperUtilities.ValidateCheckNullValue(this, nameof(messageTextTMP), messageTextTMP);
        HelperUtilities.ValidateCheckNullValue(this, nameof(canvasGroup), canvasGroup);
        HelperUtilities.ValidateCheckEnumerableValues(this, nameof(dungeonLevelList), dungeonLevelList);
    }

#endif

    #endregion Validation
}